package com.tuzhi.base.web.advice;

import com.tuzhi.common.constants.ResultConstant;
import com.tuzhi.common.domain.ResultBO;
import com.tuzhi.common.exception.BusinessException;
import com.tuzhi.common.helper.ResultHelper;
import org.apache.commons.collections.CollectionUtils;
import org.hibernate.exception.ConstraintViolationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolation;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import static com.tuzhi.common.constants.ResultConstant.ERR_BUSINESS;

//统一拦截异常
@RestControllerAdvice
public class ExceptionsHandler {

    private static final Logger log = LoggerFactory.getLogger(ExceptionsHandler.class);

    /**
     * 请求方法不支持 例如：GET/POST不对应
     * METHOD_NOT_SUPPORT的模板为"请求方法[{}]不支持，支持的方法为{}"
     *
     * @param e
     * @return
     */
    @ExceptionHandler
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public ResultBO<?> handler(HttpRequestMethodNotSupportedException e) {
        //处理异常
//        e.getMethod() Http请求过来的方法
//        List<String> supportedMethods = Arrays.asList(e.getSupportedMethods()); //支持的方法
        return ResultHelper.error(e.getMessage());
    }

    /**
     * 捕获 {@code BusinessException} 异常
     */
    @ExceptionHandler({BusinessException.class})
    public ResultBO<?> handleBusinessException(BusinessException ex) {
        return ResultHelper.error(ERR_BUSINESS.getCode(), ex.getMessage());
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(value = HttpStatus.NOT_FOUND)
    @ResponseBody
    public ResultBO handle404(NoHandlerFoundException e) {
     /*   LOGGER.info("Resource Not found, RequestURL: {}, HttpMethod: {}, Headers: {}", e.getRequestURL(),
                e.getHttpMethod(), e.getHeaders());
        LOGGER.error(e.getMessage(), e);*/
      /*  HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        return new ResponseEntity<>(new ErrorResponse(WebExceptionCode.NOT_FOUND), headers, HttpStatus.NOT_FOUND);*/
        String msg = String.format("%s,%s,Nod Found", HttpStatus.NOT_FOUND.value(), e.getRequestURL());
        return ResultHelper.error(msg);
    }
/*

    //验证参数时不符合要求
    @ResponseBody
    @ExceptionHandler(value = ConstraintViolationException.class)
    public ResultBO violationHandler(ConstraintViolationException e) {
        log.error("ConstraintViolationException: \n" + e.getMessage(), e);
        return ResultHelper.error(e.getMessage());
    }
*/

    /**
     * {@code @PathVariable} 和 {@code @RequestParam} 参数校验不通过时抛出的异常处理
     */
    @ExceptionHandler({javax.validation.ConstraintViolationException.class})
    public ResultBO<?> handleConstraintViolationException(javax.validation.ConstraintViolationException ex) {
        log.error("ConstraintViolationException: ", ex);
        /*if (StringUtils.hasText(ex.getMessage())) {
            return ResultHelper.error(ResultConstant.ERR_PARAM.getCode(), ex.getMessage());
        }*/
        Set<ConstraintViolation<?>> set = ex.getConstraintViolations();
        StringBuilder sb = new StringBuilder();
        if (CollectionUtils.isNotEmpty(set)) {
            int index = 1;
            for (ConstraintViolation<?> cv : set) {
                sb.append(index + "." + cv.getMessageTemplate());
                if (index < set.size()) {
                    sb.append("；");
                }
                ++index;
            }
        }
        return ResultHelper.error(sb.toString());
    }

    /**
     * {@code @PathVariable} 和 {@code @RequestParam} 参数校验不通过时抛出的异常处理
     */
    @ExceptionHandler({ConstraintViolationException.class})
    public ResultBO<?> handleConstraintViolationException(ConstraintViolationException ex) {
        log.error("ConstraintViolationException: ", ex);
        if (StringUtils.hasText(ex.getMessage())) {
            return ResultHelper.error(ResultConstant.ERR_PARAM.getCode(), ex.getMessage());
        }
        return ResultHelper.error(ResultConstant.ERR_PARAM.getCode());
    }

    /**
     * {@code @RequestBody} 参数校验不通过时抛出的异常处理
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public ResultBO<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        BindingResult bindingResult = ex.getBindingResult();
        StringBuilder sb = new StringBuilder();
        List<FieldError> errors = bindingResult.getFieldErrors();
        List<String> dataList = new ArrayList<>(errors.size());
        for (int i = 0; i < errors.size(); ++i) {
            FieldError fieldError = errors.get(i);
//            String msg = String.format("%s.%s:%s", i + 1, fieldError.getField(), fieldError.getDefaultMessage());
            String msg = String.format("%s.%s", i + 1, fieldError.getDefaultMessage());
            sb.append(msg);
            if (i < errors.size() - 1) {
                sb.append(";");
            }
            dataList.add(msg);
        }
        String msg = sb.toString();
        if (StringUtils.hasText(msg)) {
            return ResultHelper.error(ResultConstant.ERR_PARAM.getCode(), msg).setData(dataList);
        }
        return ResultHelper.error(ResultConstant.ERR_PARAM.getCode());
    }

    //缺少应该传递的参数
    @ResponseBody
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    public ResultBO missingParameterHandler(MissingServletRequestParameterException e) {
        log.error("MissingServletRequestParameterException: " + e.getMessage(), e);
        return ResultHelper.error(e.getMessage());
    }

    //参数类型不匹配，用户输入的参数类型有错误时会报这个
    @ResponseBody
    @ExceptionHandler(value = MethodArgumentTypeMismatchException.class)
    public ResultBO misMatchErrorHandler(MethodArgumentTypeMismatchException e) {
        log.error("MethodArgumentTypeMismatchException: \n" + e.getMessage(), e);
        return ResultHelper.error("参数类型不匹配:" + e.getMessage());
    }

    //验证时绑定错误
    @ResponseBody
    @ExceptionHandler(value = BindException.class)
    public ResultBO bindExceptionHandler(BindException ex) {
        BindingResult result = ex.getBindingResult();
        StringBuilder errorMsg = new StringBuilder();
        for (ObjectError error : result.getAllErrors()) {
            errorMsg.append(error.getDefaultMessage()).append(",");
        }
        errorMsg.delete(errorMsg.length() - 1, errorMsg.length());
//        ResponseCode.ARG_BIND_EXCEPTION.setMsg(errorMsg.toString());
        log.error("BindException: \n" + errorMsg.toString(), ex);
        return ResultHelper.error("验证时绑定错误:" + ex.getMessage());
    }

    /**
     * 通用的对异常的处理
     *
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    public ResultBO exceptionHandler(Exception e) {
        log.error("Exception: \n" + e.toString(), e);
        if (e instanceof BusinessException) {
            ResultBO resultBO = ResultHelper.error();
            BusinessException ex = (BusinessException) e;
            resultBO.setCode(ex.getCode());
            resultBO.setMsg(ex.getMsg());
            return resultBO;
        }
        return ResultHelper.error("服务器异常：" + e.getMessage());
    }

}